home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 February: Tool Chest / Apple_Developer_Group_CD_Series_February_1998_Tool_Chest.iso / Sample Code / PCI Sound Input driver / PCISoundInputDriver.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-18  |  64.6 KB  |  1,820 lines  |  [TEXT/CWIE]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Sample PCI sound input hardware driver
  5. **
  6. **    by Mark Cookson, Apple Developer Technical Support
  7. **
  8. **    File:    PCISoundInputDriver.c
  9. **
  10. **    Copyright ©1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "Apple Sample
  17. **    Code" after having made changes. If you're going to re-distribute the
  18. **    source, we require that you make it clear in the source that the code
  19. **    was descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Aliases.h>
  23. #include <DriverServices.h>
  24. #include <DriverGestalt.h>
  25. #include <NameRegistry.h>
  26. #include <Resources.h>
  27. #include <Sound.h>
  28. #include <SoundInput.h>
  29. #include <TextUtils.h>
  30. #include <Timer.h>
  31.  
  32. #include <stdio.h>
  33. #include <Strings.h>
  34.  
  35. // This is what our input parameter looks like
  36. #pragma options align=mac68k
  37. struct SoundParam {
  38.     QElemPtr                        qLink;
  39.     short                            qType;
  40.     short                            ioTrap;
  41.     Ptr                                ioCmdAddr;
  42.     IOCompletionUPP                    ioCompletion;
  43.     OSErr                            ioResult;
  44.     StringPtr                        ioNamePtr;
  45.     short                            ioVRefNum;
  46.     short                            ioCRefNum;
  47.     short                            csCode;
  48.     short                            csParam[11];
  49. };
  50. typedef struct SoundParam SoundParam, *SoundParamPtr;
  51. #pragma options align=reset
  52.  
  53. // tell the OS all about us
  54.  
  55. extern DriverDescription TheDriverDescription = {
  56.   kTheDescriptionSignature,
  57.   kInitialDriverDescriptor,
  58.   // DriverType
  59.   "\pTestSoundInputDriver",
  60.   1, 0, developStage, 1,
  61.   // DriverOSRuntimeInfo
  62.   0
  63.   | (1 * kDriverIsLoadedUponDiscovery)        /* Loader runtime options */
  64.   | (1 * kDriverIsOpenedUponLoad)            /* Opened when loaded */
  65.   | (0 * kDriverIsUnderExpertControl)        /* No I/O expert to handle loads/opens */
  66.   | (0 * kDriverIsConcurrent)                /* Not concurrent yet */
  67.   | (0 * kDriverQueuesIOPB),                /* Not internally queued yet */
  68.   "\p.TestSoundInputDriver",                /* Str31 driverName (OpenDriver param) */
  69.   0, 0, 0, 0, 0, 0, 0, 0,                    /* UInt32 driverDescReserved[8] */
  70.   // DriverOSService
  71.   1,                                        /* ServiceCount nServices */
  72.   // DriverServiceInfo
  73.   kServiceCategoryNdrvDriver,                /* OSType serviceCategory */
  74.   kNdrvTypeIsGeneric,                        /* OSType serviceType */
  75.      1, 0, developStage, 1,                    /* major, minor, stage, rev */
  76. };
  77.  
  78. //For debugging information
  79. #define DEBUG        0
  80. #define DEBUG2        0
  81. #define FULLDEBUG    0
  82.  
  83. #define kSamplesInBuffer    1024
  84. #define    kSilence            128
  85. #define    kAmplitude            75
  86.  
  87. #define    kSquareSource        1
  88. #define    kSawSource            2
  89. #define    kSilenceSource        3
  90.  
  91. #define kNumRates            3
  92. #define kNumSizes            2
  93. #define kNumCompressions    1
  94.  
  95. typedef struct myTMTask {
  96.     TMTask        theTask;
  97.     ParmBlkPtr    pb;
  98. } myTMTask;
  99.  
  100.  
  101. //----------------------------------------------------------------------------------
  102. //                                    Function Prototypes
  103. //----------------------------------------------------------------------------------
  104.  
  105. pascal OSErr FragInit                (CFragInitBlockPtr initInfo);
  106. pascal void FragTerm                (void);
  107.  
  108. static OSStatus DriverKillIOCmd     (ParmBlkPtr pb);
  109. extern OSErr DoDriverIO                (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind);
  110.  
  111. static pascal void HWIntProc        (QElemPtr passedPtr);
  112. static void MakeSquareWave            (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels);
  113. static void MakeSawWave                (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels);
  114. static OSErr DoOptionsDialog        (void);
  115. static void SetHardwareToDefault    (void);
  116.  
  117.  
  118. //----------------------------------------------------------------------------------
  119. //                                    Globals
  120. //----------------------------------------------------------------------------------
  121.  
  122. //Hardware specific globals
  123. SInt16            gContinuousOn,
  124.                 gLevelMeterOn,
  125.                 gTwosComplementOn,
  126.                 gAGCOn,
  127.                 gPlayThruVolume,
  128.                 gVoxRecordingOn,
  129.                 gVoxStoppingOn,
  130.                 gPauseState,
  131.                 gNumberChannels,
  132.                 gSampleSize,
  133.                 gVOXStartTrigger,
  134.                 gVOXStopTrigger,
  135.                 gVOXDelay,
  136.                 gCompressionFactor,
  137.                 gCurrentInputSource        = kSquareSource;
  138. SInt32            gActiveChannels;            //channels to record from 0x01 == only left, 0x02 == only right, 0x03 == both channels
  139. UnsignedFixed    gSampleRate;
  140. Fixed            gInputGain,
  141.                 gLeftInputGain,
  142.                 gRightInputGain;
  143. OSType            gCompressionType,
  144.                 gRecordingQuality;
  145. SIInterruptUPP    gUserInterruptProc        = nil;
  146.  
  147. //Globals needed to simulate our hardware
  148. myTMTask        gSimulatedHWInterrupt;
  149. TimerUPP        gSimulatedHWIntProc        = nil;
  150. SInt32            gInterruptFreq            = 0,
  151.                 gSamplesWritten            = 0;
  152. SInt16            gNextSaw16Sample        = -((kAmplitude * 0.01) * 32767);    //starting point for saw tooth wave
  153. SInt8            gNextSaw8Sample            = kSilence - kAmplitude;    //starting point for saw tooth wave
  154. Ptr                gSoundBuffer            = nil;    //our hardware's internal buffer
  155.                                                 
  156. //Driver specific globals
  157. Boolean            gStopRecording            = false;
  158. DriverRefNum    gDrvrRefNum;
  159. Handle            gDrvrIcon,
  160.                 gInputSourceNames;
  161. AliasHandle        gAliasToDriver;
  162. StringHandle    gDriverName;
  163.  
  164. //----------------------------------------------------------------------------------
  165. //                                    CFM Init
  166. //----------------------------------------------------------------------------------
  167.  
  168. pascal OSErr FragInit (CFragInitBlockPtr initInfo) {
  169.     Handle                iconHandle,
  170.                         strHandle;
  171.     StringHandle        driverName;
  172.     SInt16                resRefNum;
  173.     OSErr                err            = noErr;
  174.  
  175.     if (initInfo->fragLocator.where == kOnDiskFlat || initInfo->fragLocator.where == kOnDiskSegmented) {
  176.         resRefNum = FSpOpenResFile (initInfo->fragLocator.u.onDisk.fileSpec, fsRdPerm);
  177.         err = ResError();
  178.         if (err != noErr) {
  179.         #if DEBUG
  180.             SysDebugStr ("\p!!Error returned from FSpOpenResFile!!");
  181.         #endif
  182.         }
  183.  
  184.         if (resRefNum == -1) {
  185.         #if DEBUG
  186.             SysDebugStr ("\p!!Error resRefNum is -1!!");
  187.         #endif
  188.         }
  189.     }
  190.  
  191.     if (resRefNum != -1 && err == noErr) {
  192.         iconHandle = Get1Resource ('ICN#', 128);
  193.         err = ResError ();
  194.         if (iconHandle == nil) {
  195.         #if DEBUG
  196.             SysDebugStr ("\p!!iconHandle is nil!!");
  197.         #endif
  198.         }
  199.  
  200.         if (err != noErr) {
  201.         #if DEBUG
  202.             SysDebugStr ("\p!!Error from Get1Resource('ICN#', 128)!!");
  203.         #endif
  204.         }
  205.  
  206.         if (iconHandle != nil) {
  207.             gDrvrIcon = NewHandleSys (GetHandleSize (iconHandle));
  208.             BlockMoveData (*iconHandle, *gDrvrIcon, GetHandleSize (iconHandle));
  209.             ReleaseResource (iconHandle);
  210.         } else if (err == noErr) {
  211.             err = resNotFound;
  212.         #if DEBUG
  213.             SysDebugStr ("\p!!Error Get1Resource('ICN#', 128), resNotFound!!");
  214.         #endif
  215.         }
  216.  
  217.         strHandle = Get1Resource ('STR#', 128);
  218.         err = ResError ();
  219.         if (strHandle == nil) {
  220.         #if DEBUG
  221.             SysDebugStr ("\p!!strHandle is nil!!");
  222.         #endif
  223.         }
  224.  
  225.         if (err != noErr) {
  226.         #if DEBUG
  227.             SysDebugStr ("\p!!Error from Get1Resource('STR#', 128)!!");
  228.         #endif
  229.         }
  230.  
  231.         if (strHandle != nil) {
  232.             gInputSourceNames = NewHandleSys (GetHandleSize (strHandle));
  233.             BlockMoveData (*strHandle, *gInputSourceNames, GetHandleSize (strHandle));
  234.             ReleaseResource (strHandle);
  235.         } else if (err == noErr) {
  236.             err = resNotFound;
  237.         #if DEBUG
  238.             SysDebugStr ("\p!!Error Get1Resource('STR#', 128), resNotFound!!");
  239.         #endif
  240.         }
  241.  
  242.         driverName = GetString (128);
  243.         err = ResError ();
  244.         if (driverName == nil) {
  245.         #if DEBUG
  246.             SysDebugStr ("\p!!driverName is nil!!");
  247.         #endif
  248.         }
  249.  
  250.         if (err != noErr) {
  251.         #if DEBUG
  252.             SysDebugStr ("\p!!Error from GetString(128)!!");
  253.         #endif
  254.         }
  255.  
  256.         if (driverName != nil) {
  257.             gDriverName = (StringHandle)NewHandleSys (GetHandleSize ((Handle)driverName));
  258.             BlockMoveData (*driverName, *gDriverName, GetHandleSize ((Handle)driverName));
  259.             ReleaseResource ((Handle)driverName);
  260.         } else if (err == noErr) {
  261.             err = resNotFound;
  262.         #if DEBUG
  263.             SysDebugStr ("\p!!Error GetString(128), resNotFound!!");
  264.         #endif
  265.         }
  266.  
  267.         CloseResFile (resRefNum);
  268.     }
  269.  
  270.     if (err == noErr) {
  271.         //make an alias to ourselves so that we can open our driver later if needed
  272.         err = NewAlias (nil, initInfo->fragLocator.u.onDisk.fileSpec, &gAliasToDriver);
  273.     }
  274.  
  275. #if DEBUG
  276.     if (err != noErr) {
  277.         SysDebugStr ("\p!!Error in FragInit!!");
  278.     }
  279. #endif
  280.  
  281.     return (err);
  282. }
  283.  
  284. pascal void FragTerm (void) {
  285.     if (gDrvrIcon != nil) {
  286.         DisposeHandle (gDrvrIcon);
  287.         gDrvrIcon = nil;
  288.     }
  289.  
  290.     if (gInputSourceNames != nil) {
  291.         DisposeHandle (gInputSourceNames);
  292.         gInputSourceNames = nil;
  293.     }
  294.  
  295.     if (gAliasToDriver != nil) {
  296.         DisposeHandle ((Handle)gAliasToDriver);
  297.         gAliasToDriver = nil;
  298.     }
  299. }
  300.  
  301. //----------------------------------------------------------------------------------
  302. //                                    Driver calls
  303. //----------------------------------------------------------------------------------
  304.  
  305. /*
  306.     Always run at task level, can allocate and move memory.
  307. */
  308. static OSStatus DriverInitializeCmd (AddressSpaceID addressSpaceID, DriverInitInfoPtr initialInfo) {
  309. #pragma unused (addressSpaceID, initialInfo)
  310.     OSStatus            err = noErr;
  311.  
  312. #if DEBUG
  313.     SysDebugStr ("\pin DriverInitializeCmd;g");
  314. #endif
  315.  
  316.     gDrvrRefNum = initialInfo->refNum;
  317.  
  318. #if DEBUG
  319.     if (err != noErr) {
  320.         SysDebugStr ("\p!!Error in DriverInitializeCmd!!");
  321.     }
  322. #if FULLDEBUG
  323.     SysDebugStr ("\pleaving DriverInitializeCmd;g");
  324. #endif
  325. #endif
  326.  
  327.     return (err);
  328. }
  329.  
  330. /*
  331.     Always run at task level, can allocate and move memory.
  332. */
  333. static OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo) {
  334. #pragma unused (finalInfo)
  335.     OSStatus            err = noErr;
  336.  
  337. #if DEBUG
  338.     SysDebugStr ("\pin DriverFinalizeCmd;g");
  339. #endif
  340.  
  341. #if DEBUG
  342.     if (err != noErr) {
  343.         SysDebugStr ("\p!!Error in DriverFinalizeCmd!!");
  344.     }
  345. #if FULLDEBUG
  346.     SysDebugStr ("\pleaving DriverFinalizeCmd;g");
  347. #endif
  348. #endif
  349.  
  350.     return (err);
  351. }
  352.  
  353. /*
  354.     Always run at task level, can allocate and move memory.
  355. */
  356. static OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo) {
  357. #pragma unused (supersededInfo)
  358.  
  359.     OSErr            err = noErr;
  360.  
  361. #if DEBUG
  362.     SysDebugStr ("\pin DriverSupersededCmd;g");
  363. #endif
  364.  
  365. #if DEBUG
  366.     if (err != noErr) {
  367.         SysDebugStr ("\p!!Error in DriverSupersededCmd!!");
  368.     }
  369. #if FULLDEBUG
  370.     SysDebugStr ("\pleaving DriverSupersededCmd;g");
  371. #endif
  372. #endif
  373.  
  374.     return (err);
  375. }
  376.  
  377. /*
  378.     Always run at task level, can allocate and move memory.
  379. */
  380. static OSStatus DriverReplaceCmd (AddressSpaceID addressSpaceID, DriverReplaceInfoPtr replaceInfo) {
  381. #pragma unused (addressSpaceID, replaceInfo)
  382.     OSStatus            err = noErr;
  383.  
  384. #if DEBUG
  385.     SysDebugStr ("\pin DriverReplaceCmd;g");
  386. #endif
  387.  
  388. #if DEBUG
  389.     if (err != noErr) {
  390.         SysDebugStr ("\p!!Error in DriverReplaceCmd!!");
  391.     }
  392. #if FULLDEBUG
  393.     SysDebugStr ("\pleaving DriverReplaceCmd;g");
  394. #endif
  395. #endif
  396.  
  397.     return (err);
  398. }
  399.  
  400. /*
  401.     Always run at task level, can allocate and move memory.
  402. */
  403. static OSStatus DriverOpenCmd (AddressSpaceID addressSpaceID, ParmBlkPtr pb) {
  404. #pragma unused (addressSpaceID, pb)
  405.     OSStatus            err = noErr;
  406.  
  407. #if DEBUG
  408.     SysDebugStr ("\pin DriverOpenCmd;g");
  409. #endif
  410.  
  411.     gSoundBuffer = NewPtrSys (kSamplesInBuffer * 4);    //room for 16 bit stereo samples
  412.  
  413.     if (gSoundBuffer != nil) {
  414.         gSimulatedHWIntProc = NewTimerProc (HWIntProc);
  415.         SetHardwareToDefault ();
  416.  
  417.         err = SPBSignInDevice (gDrvrRefNum, *gDriverName);
  418.     } else {
  419.     #if DEBUG
  420.         SysDebugStr ("\p!!couldn't allocate memory for our buffer!!");
  421.     #endif
  422.         err = MemError ();
  423.     }
  424.  
  425. #if DEBUG
  426.     if (err != noErr) {
  427.         SysDebugStr ("\p!!Error in DriverOpenCmd!!");
  428.     }
  429. #if FULLDEBUG
  430.     SysDebugStr ("\pleaving DriverOpenCmd;g");
  431. #endif
  432. #endif
  433.  
  434.     return (err);
  435. }
  436.  
  437. /*
  438.     Always run at task level, can allocate and move memory.
  439. */
  440. static OSStatus DriverCloseCmd (ParmBlkPtr pb) {
  441. #pragma unused (pb)
  442.     OSStatus            err = noErr;
  443.  
  444. #if DEBUG
  445.     SysDebugStr ("\pin DriverCloseCmd;g");
  446. #endif
  447.  
  448.     (void)SPBSignOutDevice (gDrvrRefNum);
  449.     DisposePtr (gSoundBuffer);
  450.     gSoundBuffer = nil;
  451.     DisposeRoutineDescriptor (gSimulatedHWIntProc);
  452.     gSimulatedHWIntProc = nil;
  453.  
  454. #if FULLDEBUG
  455.     SysDebugStr ("\pleaving DriverCloseCmd;g");
  456. #endif
  457.  
  458.     return (err);
  459. }
  460.  
  461. /*
  462.     May run at interrupt level, CANNOT allocate or move memory.
  463. */
  464. static OSStatus DriverControlCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) {
  465. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  466.     OSStatus            err            = noErr;
  467.     OSType                selector    = 0;
  468.  
  469. #if FULLDEBUG
  470.     SysDebugStr ("\pin DriverControlCmd;g");
  471. #endif
  472.  
  473.     if (pb->csCode == 2) {
  474.         selector = ((OSType*)pb->csParam)[0];
  475.     }
  476.  
  477.     switch (selector) {
  478.         //Control calls that are required to be supported
  479.         case siCompressionType:
  480.             //Set the compression type. Some devices allow the incoming samples to be
  481.             //compressed before being placed in your application’s input buffer. The
  482.             //infoData parameter points to a buffer of type OSType, which is the
  483.             //compression type.
  484.         #if DEBUG2
  485.             SysDebugStr ("\pcontrol: siCompressionType");
  486.         #endif
  487.             if (((OSType*)pb->csParam)[1] != 'NONE') {
  488.                 err = siInvalidCompression;    //we don't support compression
  489.             }
  490.             break;
  491.         case siContinuous:
  492.             //Set the state of continuous recording from this device. If recording
  493.             //is being turned off, the driver stops recording samples to its internal buffer.
  494.             //Only sound input device drivers that support asynchronous recording support
  495.             //continuous recording. The infoData parameter points to an integer, which is
  496.             //the state of continuous recording (0 is off, 1 is on).
  497.         #if DEBUG2
  498.             SysDebugStr ("\pcontrol: siContinuous");
  499.         #endif
  500.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  501.                 gContinuousOn = ((SInt16*)pb->csParam)[2];
  502.             } else {
  503.                 err = paramErr;
  504.             }
  505.             break;
  506.         case siLevelMeterOnOff:
  507.             //Set the current state of the level meter. For calls to set the level meter,
  508.             //the infoData parameter points to an integer that indicates whether the level meter
  509.             //is off (0) or on (1).
  510.         #if DEBUG2
  511.             SysDebugStr ("\pcontrol: siLevelMeterOnOff");
  512.         #endif
  513.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  514.                 gLevelMeterOn = ((SInt16*)pb->csParam)[2];
  515.             } else {
  516.                 err = paramErr;
  517.             }
  518.             break;
  519.         case siNumberChannels:
  520.             //Set the number of channels this device is to record. The infoData parameter points
  521.             //to an integer, which indicates the number of channels. Note that this selector
  522.             //determines the format of the data stream output by the driver. If the number of
  523.             //channels is 1, the driver should output monophonic data in response to a Read call.
  524.             //If the number of channels is 2, the driver should output interleaved stereo data.
  525.         #if DEBUG2
  526.             SysDebugStr ("\pcontrol: siNumberChannels");
  527.         #endif
  528.             if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 2) {
  529.                 gNumberChannels = ((SInt16*)pb->csParam)[2];
  530.             } else {
  531.                 err = notEnoughHardwareErr;
  532.             }
  533.             break;
  534.         case siRecordingQuality:
  535.             //Set the current quality of recorded sound. The infoData parameter points to a buffer
  536.             //of type OSType, which is the recording quality. Currently foud qualities are supported,
  537.             //defined by these constants:
  538.             //            siCDQuality                            = 'cd  ',                /*44.1kHz, stereo, 16 bit*/
  539.             //            siBestQuality                        = 'best';                /*22kHz, mono, 8 bit*/
  540.             //            siBetterQuality                        = 'betr';                /*22kHz, mono, MACE 3:1*/
  541.             //            siGoodQuality                        = 'good';
  542.             //These qualities are defined by the sound input device driver. Usually best means
  543.             //monaural, 8-bit, 22 kHz, sound with no compression.
  544.         #if DEBUG2
  545.             SysDebugStr ("\pcontrol: siRecordingQuality");
  546.         #endif
  547.             switch (((OSType*)pb->csParam)[1]) {
  548.                 case siCDQuality:
  549.                     gRecordingQuality = siCDQuality;
  550.                     gSampleRate = rate44khz;
  551.                     gSampleSize = 16;
  552.                     gNumberChannels = 2;
  553.                     gCompressionType = 'NONE';
  554.                     break;
  555.                 case siBestQuality:
  556.                     gRecordingQuality = siBestQuality;
  557.                     gSampleRate = rate22050hz;
  558.                     gSampleSize = 8;
  559.                     gNumberChannels = 1;
  560.                     gCompressionType = 'NONE';
  561.                     break;
  562.                 case siBetterQuality:
  563.                     gRecordingQuality = siBetterQuality;
  564.                     gSampleRate = rate22050hz;
  565.                     gSampleSize = 8;
  566.                     gNumberChannels = 1;
  567.                     gCompressionType = 'NONE';
  568.                     break;
  569.                 case siGoodQuality:
  570.                     gRecordingQuality = siGoodQuality;
  571.                     gSampleRate = rate11025hz;
  572.                     gSampleSize = 8;
  573.                     gNumberChannels = 1;
  574.                     gCompressionType = 'NONE';
  575.                     break;
  576.                 default:
  577.                     err = siUnknownQuality;
  578.             }
  579.             break;
  580.         case siSampleRate:
  581.             //Set the sample rate to be produced by this device. The sample rate must be in the
  582.             //range 0 to 65535.65535 Hz. The sample rate is declared as a Fixed data type. In
  583.             //order to accommodate sample rates greater than 32 kHz, the most significant bit is
  584.             //not treated as a sign bit; instead, that bit is interpreted as having the value 32,768.
  585.             //The infoData parameter points to a buffer of type Fixed, which is the sample rate.
  586.         #if DEBUG2
  587.             SysDebugStr ("\pcontrol: siSampleRate");
  588.         #endif
  589.             if (((UnsignedFixed*)pb->csParam)[1] == rate44khz || ((UnsignedFixed*)pb->csParam)[1] == rate22050hz || ((UnsignedFixed*)pb->csParam)[1] == rate11025hz) {
  590.                 gSampleRate = ((UnsignedFixed*)pb->csParam)[1];
  591.             } else {
  592.                 err = siInvalidSampleRate;
  593.             }
  594.             break;
  595.         case siSampleSize:
  596.             //Set the sample size to be produced by this device. Because some compression formats
  597.             //require specific sample sizes, this selector might return an error when compression
  598.             //is used. The infoData parameter points to an integer, which is the sample size.
  599.         #if DEBUG2
  600.             SysDebugStr ("\pcontrol: siSampleSize");
  601.         #endif
  602.             if (((SInt16*)pb->csParam)[2] == 8 || ((SInt16*)pb->csParam)[2] == 16) {
  603.                 gSampleSize = ((SInt16*)pb->csParam)[2];
  604.             } else {
  605.                 err = siInvalidSampleSize;
  606.             }
  607.             break;
  608.         case siTwosComplementOnOff:
  609.             //Set the current state of the two’s complement feature. This selector only applies to
  610.             //8-bit data. (16-bit samples are always stored in two’s complement format.) If on, the
  611.             //driver stores all samples in the application buffer as two’s complement values
  612.             //(that is, –128 to 127). Otherwise, the driver stores the samples as offset binary
  613.             //values (that is, 0 to 255). The infoData parameter points to an integer, which is the
  614.             //current state of the two’s complement feature (1 if two’s complement output is desired,
  615.             //0 otherwise).
  616.         #if DEBUG2
  617.             SysDebugStr ("\pcontrol: siTwosComplementOnOff");
  618.         #endif
  619.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  620.                 gTwosComplementOn = ((SInt16*)pb->csParam)[2];
  621.             } else {
  622.                 err = paramErr;
  623.             }
  624.             break;
  625.  
  626.         //Sound Manager only calls
  627.         case siCloseDriver:
  628.             //The Sound Input Manager sends this selector when it closes a device previously
  629.             //opened with write permission. The sound input device driver should stop any
  630.             //recording in progress, deallocate the input hardware, and initialize local
  631.             //variables to default settings.
  632.         #if DEBUG2
  633.             SysDebugStr ("\pcontrol: siCloseDriver");
  634.         #endif
  635.             //We will actually stop the hardware at the next hardware interrupt
  636.             gStopRecording = true;
  637.             break;
  638.         case siInitializeDriver:
  639.             //The Sound Input Manager sends this selector when it opens a sound input device
  640.             //with write permission. The sound input device driver initializes local variables
  641.             //and prepares to start recording. If possible, the driver initializes the device
  642.             //to a sampling rate of 22 kHz, a sample size of 8 bits, mono recording, no compression,
  643.             //automatic gain control on, and all other features off.
  644.         #if DEBUG2
  645.             SysDebugStr ("\pcontrol: siInitializeDriver");
  646.         #endif
  647.             SetHardwareToDefault ();
  648.             break;
  649.         case siPauseRecording:
  650.             //The Sound Input Manager uses this selector to set the current pause state.
  651.             //The sound input device driver continues recording but does not store the sampled
  652.             //data in a buffer. The infoData parameter points to an integer, which indicates
  653.             //the state of pausing (0 is off, 1 is on).
  654.         #if DEBUG2
  655.             SysDebugStr ("\pcontrol: siPauseRecording");
  656.         #endif
  657.             gPauseState = ((SInt16*)pb->csParam)[2];
  658.             break;
  659.         case siUserInterruptProc:
  660.             //The Sound Input Manager sends this selector to specify the sound input interrupt
  661.             //routine that the sound input device driver should call. The infoData parameter
  662.             //points to a procedure pointer, which is the address of the sound input interrupt routine.
  663.         #if DEBUG2
  664.             SysDebugStr ("\pcontrol: siUserInterruptProc");
  665.         #endif
  666.             gUserInterruptProc = ((SIInterruptUPP*)pb->csParam)[1];
  667.             break;
  668.  
  669.         //Control calls that can be optionally supported
  670.         case siActiveChannels:
  671.             //Set the channels to record from. When setting the active channels, the data passed
  672.             //in is a long integer that is interpreted as a bitmap describing the channels to record
  673.             //from. For example, if bit 0 is set, then the first channel is made active. The samples
  674.             //for each active channel are interleaved in the application’s buffer.
  675.         #if DEBUG2
  676.             SysDebugStr ("\pcontrol: siActiveChannels");
  677.         #endif
  678.             if (((SInt32*)pb->csParam)[1] >= 0x01 && ((SInt32*)pb->csParam)[1] <= 0x03) {
  679.                 gActiveChannels = ((SInt32*)pb->csParam)[1];
  680.             } else {
  681.                 err = notEnoughHardwareErr;
  682.             }
  683.             break;
  684.         case siAGCOnOff:
  685.             //Set the current state of the automatic gain control feature. The infoData parameter
  686.             //points to an integer, which is 0 if gain control is off and 1 if it is on.
  687.         #if DEBUG2
  688.             SysDebugStr ("\pcontrol: siAGCOnOff");
  689.         #endif
  690.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  691.                 gAGCOn = ((SInt16*)pb->csParam)[2];
  692.             } else {
  693.                 err = paramErr;
  694.             }
  695.             break;
  696.         case siInputGain:
  697.             //Set the current sound input gain. If the available hardware allows adjustment of the
  698.             //recording gain, this selector lets you get and set the gain. In response to a Control
  699.             //call, a sound input driver sets the gain level used for all subsequent recording to the
  700.             //specified value. The infoData parameter points to a 4-byte value of type Fixed ranging
  701.             //from 0.5 to 1.5, where 1.5 specifies maximum gain.
  702.         #if DEBUG2
  703.             SysDebugStr ("\pcontrol: siInputGain");
  704.         #endif
  705.             if (((Fixed*)pb->csParam)[1] >= (Fixed)0.5 && ((Fixed*)pb->csParam)[1] <= (Fixed)1.5) {
  706.                 gInputGain = ((Fixed*)pb->csParam)[1];
  707.             } else {
  708.                 err = paramErr;
  709.             }
  710.             break;
  711.         case siInputSource:
  712.             //Set the current sound input source. If the available hardware allows recording from more
  713.             //than one source, this selector lets you get and set the source. In response to a Control
  714.             //call, a sound input driver sets the source of all subsequent recording to the value passed
  715.             //in. If the value is less than 1 or greater than the number of input sources, the driver
  716.             //returns paramErr; if the driver supports only one source, it returns siUnknownInfoType. The
  717.             //infoData parameter points to an integer, which is the index of the current sound input source.
  718.         #if DEBUG2
  719.             SysDebugStr ("\pcontrol: siInputSource");
  720.         #endif
  721.             if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 3) {
  722.                 gCurrentInputSource = ((SInt16*)pb->csParam)[2];
  723.             } else {
  724.                 err = notEnoughHardwareErr;
  725.             }
  726.             break;
  727.         case siOptionsDialog:
  728.             //Cause the driver to display the Options dialog box (SPBSetDeviceInfo). This dialog box
  729.             //is designed to allow the user to configure device-specific features of the sound input
  730.             //hardware. With SPBSetDeviceInfo, the infoData parameter is unused.
  731.         #if DEBUG2
  732.             SysDebugStr ("\pcontrol: siOptionsDialog");
  733.         #endif
  734.             err = DoOptionsDialog ();
  735.             break;
  736.         case siPlayThruOnOff:
  737.             //Set the current play-through state and volume. The infoData parameter points to an integer,
  738.             //which indicates the current play-through volume (1 to 7). If that integer is 0, then
  739.             //play-through is off.
  740.         #if DEBUG2
  741.             SysDebugStr ("\pcontrol: siPlayThruOnOff");
  742.         #endif
  743.             err = notEnoughHardwareErr;
  744.             //    If our (fake) hardware actually did playthrough, this would be useful...
  745.         /*
  746.             if (((short*)pb->csParam)[2] >= 0 || ((short*)pb->csParam)[2] <= 7) {
  747.                 gPlayThruVolume = ((short*)pb->csParam)[2];
  748.             } else {
  749.                 err = notEnoughHardwareErr;
  750.             }
  751.         */
  752.             break;
  753.         case siStereoInputGain:
  754.             //Set the current stereo sound input gain. If the available hardware allows adjustment of
  755.             //the recording gain, this selector lets you get and set the gain for each of two channels
  756.             //(left or right). In response to a Status call, a sound input driver should return the
  757.             //current gain setting for the specified channel. In response to a Control call, a sound input
  758.             //driver should set the gain level used for all subsequent recording to the specified value.
  759.             //The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5,
  760.             //where 1.5 specifies maximum gain. The first of these values is equivalent to the gain for
  761.             //the left channel and the second value is equivalent to the gain for the right channel.
  762.         #if DEBUG2
  763.             SysDebugStr ("\pcontrol: siStereoInputGain");
  764.         #endif
  765.             if (((Fixed*)pb->csParam)[1] <= 1.5 && ((Fixed*)pb->csParam)[1] >= 0.5 && ((Fixed*)pb->csParam)[2] <= 1.5 && ((Fixed*)pb->csParam)[2] >= 0.5) {
  766.                 gLeftInputGain = ((Fixed*)pb->csParam)[1];
  767.                 gRightInputGain = ((Fixed*)pb->csParam)[2];
  768.             } else {
  769.                 err = paramErr;
  770.             }
  771.             break;
  772.         case siVoxRecordInfo:
  773.             //Set the current VOX recording parameters. The infoData parameter points to two integers.
  774.             //The first integer indicates whether VOX recording is on or off (0 if off, 1 if on). The
  775.             //second integer indicates the VOX record trigger value. Trigger values range from 0 to 255
  776.             //(0 is trigger immediately, 255 is trigger only on full volume).
  777.         #if DEBUG2
  778.             SysDebugStr ("\pcontrol: siVoxRecordInfo");
  779.         #endif
  780.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  781.                 gVoxRecordingOn = ((SInt16*)pb->csParam)[2];
  782.             } else {
  783.                 err = paramErr;
  784.             }
  785.  
  786.             if (gVoxRecordingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) {
  787.                 gVOXStartTrigger = ((SInt16*)pb->csParam)[3];
  788.             } else {
  789.                 err = paramErr;
  790.             }
  791.             break;
  792.         case siVoxStopInfo:
  793.             //Set the current VOX stopping parameters. The infoData parameter points to three integers.
  794.             //The first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The
  795.             //second integer indicates the VOX stop trigger value. Trigger values range from 0 to 255
  796.             //(255 is stop immediately, 0 is stop only on total silence). The third integer indicates
  797.             //how many milliseconds the trigger value must be continuously valid for recording to be stopped.
  798.             //Delay values range from 0 to 65,535.
  799.         #if DEBUG2
  800.             SysDebugStr ("\pcontrol: siVoxStopInfo");
  801.         #endif
  802.             if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
  803.                 gVoxStoppingOn = ((SInt16*)pb->csParam)[2];
  804.             } else {
  805.                 err = paramErr;
  806.             }
  807.  
  808.             if (gVoxStoppingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) {
  809.                 gVOXStopTrigger = ((SInt16*)pb->csParam)[3];
  810.                 gVOXDelay = ((SInt16*)pb->csParam)[4];
  811.             } else {
  812.                 err = paramErr;
  813.             }
  814.             break;
  815.  
  816.         //Unknown or unsupported control call
  817.         default:
  818.         #if DEBUG
  819.             SysDebugStr ("\pcontrol: unknown selector");
  820.         #endif
  821.             err = controlErr;
  822.     }
  823.  
  824. #if DEBUG
  825.     if (err != noErr && err != controlErr) {
  826.         SysDebugStr ("\p!!Error in DriverControlCmd!!");
  827.     }
  828. #if FULLDEBUG
  829.     SysDebugStr ("\pleaving DriverControlCmd;g");
  830. #endif
  831. #endif
  832.  
  833.     return (err);
  834. }
  835.  
  836. /*
  837.     May run at interrupt level, CANNOT allocate or move memory.
  838. */
  839. static OSStatus DriverStatusCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) {
  840. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  841.     OSStatus            err            = noErr;
  842.     OSType                selector    = 0;
  843.     Handle                iconHandle,
  844.                         compressionAvailable,
  845.                         sampleRateAvailable,
  846.                         sampleSizeAvailable;
  847.     SoundInfoList        info;
  848.  
  849. #if FULLDEBUG
  850.     SysDebugStr ("\pin DriverStatusCmd;g");
  851. #endif
  852.  
  853.     if (pb->csCode == 2) {
  854.         selector = ((OSType*)pb->csParam)[0];
  855.     }
  856.  
  857.     switch (selector) {
  858.         //Status calls that are required to be supported
  859.         case siAsync:
  860.             //Determine whether the driver supports asynchronous recording functions. The infoData parameter
  861.             //points to an integer, which is 0 if the driver supports synchronous calls only and 1 otherwise.
  862.             //Some sound input drivers do not support asynchronous recording at all, and some might support
  863.             //asynchronous recording only on certain hardware configurations.
  864.         #if DEBUG2
  865.             SysDebugStr ("\pstatus: siAsync");
  866.         #endif
  867.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  868.             ((SInt16*)pb->csParam)[2] = 1;    //we can do async recording
  869.             break;
  870.         case siChannelAvailable:
  871.             //Get the maximum number of channels this device can record. The infoData parameter points to an
  872.             //integer, which is the number of available channels.
  873.         #if DEBUG2
  874.             SysDebugStr ("\pstatus: siChannelAvailable");
  875.         #endif
  876.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  877.             ((SInt16*)pb->csParam)[2] = 2;    //we can do stereo (note: Sound Manager doesn't currently work with more than 2 channels)
  878.             break;
  879.         case siCompressionAvailable:
  880.             //Get the number and list of compression types this device can produce. The infoData parameter
  881.             //points to an integer, which is the number of compression types, followed by a handle. The
  882.             //handle references a list of compression types, each of type OSType.
  883.         #if DEBUG2
  884.             SysDebugStr ("\pstatus: siCompressionAvailable");
  885.         #endif
  886.             compressionAvailable = NewHandle (sizeof (OSType) * kNumCompressions);
  887.             if (compressionAvailable != nil) {
  888.                 ((OSType*)*compressionAvailable)[0] = 'NONE';
  889.                 info.count = kNumCompressions;
  890.                 info.infoHandle = compressionAvailable;
  891.                 ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList);        //number of bytes being returned
  892.                 *(SoundInfoList*)(pb->csParam+2) = info;
  893.             }
  894.             break;
  895.         case siCompressionFactor:
  896.             //Get the compression factor of the current compression type. For example, the compression factor
  897.             //for MACE 3:1 compression is 3. If a sound input device driver supports only compression type 'NONE',
  898.             //the returned compression type is 1. The infoData parameter points to an integer, which is the
  899.             //compression factor.
  900.         #if DEBUG2
  901.             SysDebugStr ("\pstatus: siCompressionFactor");
  902.         #endif
  903.             if (gCompressionType == 'NONE') {
  904.                 ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  905.                 ((SInt16*)pb->csParam)[2] = 1;    //no compression equals 1:1
  906.             }
  907.             break;
  908.         case siCompressionType:
  909.             //Get the compression type. Some devices allow the incoming samples to be compressed before being
  910.             //placed in your application’s input buffer. The infoData parameter points to a buffer of type
  911.             //OSType, which is the compression type.
  912.         #if DEBUG2
  913.             SysDebugStr ("\pstatus: siCompressionType");
  914.         #endif
  915.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  916.             ((OSType*)pb->csParam)[1] = gCompressionType;
  917.             break;
  918.         case siContinuous:
  919.             //Get the state of continuous recording from this device. If recording is being turned off, the
  920.             //driver stops recording samples to its internal buffer. Only sound input device drivers that
  921.             //support asynchronous recording support continuous recording. The infoData parameter points to an
  922.             //integer, which is the state of continuous recording (0 is off, 1 is on).
  923.         #if DEBUG2
  924.             SysDebugStr ("\pstatus: siContinuous");
  925.         #endif
  926.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  927.             ((SInt16*)pb->csParam)[2] = gContinuousOn;
  928.             break;
  929.         case siDeviceBufferInfo:
  930.             //Get the size of the device’s internal buffer. This information can be useful when you want to
  931.             //modify sound input data at interrupt time. Note, however, that if a driver is recording continuously,
  932.             //then the size of the buffer passed to your sound input interrupt routine might be greater than the
  933.             //size this selector returns because data recorded between calls to SPBRecord as well as recorded
  934.             //during calls to SPBRecord will be sent to your interrupt routine. The infoData parameter points to
  935.             //a long integer, which is the size of the device’s internal buffer.
  936.         #if DEBUG2
  937.             SysDebugStr ("\pstatus: siDeviceBufferInfo");
  938.         #endif
  939.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  940.             ((SInt32*)pb->csParam)[1] = kSamplesInBuffer * gNumberChannels * (gSampleSize / 8);
  941.             break;
  942.         case siDeviceConnected:
  943.             //Get the state of the device connection. The infoData parameter points to an integer, which is one
  944.             //of the following constants:
  945.             //            siDeviceIsConnected                    = 1;
  946.             //            siDeviceNotConnected                = 0;
  947.             //            siDontKnowIfConnected                = -1;
  948.             //The siDeviceIsConnected constant indicates that the device is connected and ready. The
  949.             //siDeviceNotConnected constant indicates that the device is not connected. The siDontKnowIfConnected
  950.             //constant indicates that the Sound Input Manager cannot determine whether the device is connected.
  951.         #if DEBUG2
  952.             SysDebugStr ("\pstatus: siDeviceConnected");
  953.         #endif
  954.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  955.             ((SInt16*)pb->csParam)[2] = siDeviceIsConnected;
  956.             break;
  957.         case siDeviceIcon:
  958.             //Get the device’s icon and icon mask. In response to a Status call, a sound input device driver
  959.             //should return, in the location specified by the infoData parameter, a handle to a block of memory
  960.             //that contains the icon and its mask in the format of an 'ICN#' resource. It is the driver’s
  961.             //responsibility to allocate that block of memory, but it should not releasee it. The software issuing
  962.             //this selector is responsible for disposing of the handle. As a result, a device driver should detach
  963.             //any resource handles (by calling DetachResource) before returning them to the caller.
  964.         #if DEBUG2
  965.             SysDebugStr ("\pstatus: siDeviceIcon");
  966.         #endif
  967.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  968.             iconHandle = gDrvrIcon;
  969.             err = HandToHand (&iconHandle);
  970.             if (err != noErr) {
  971.                 SysDebugStr ("\pHandToHand returned an error");
  972.             }
  973.             ((Handle*)pb->csParam)[1] = iconHandle;
  974.             break;
  975.         case siDeviceName:
  976.             //Get the name of the sound input device. Your application must pass a pointer to a buffer that will
  977.             //be filled in with the device’s name. The buffer needs to be large enough to hold a Str255 data type.
  978.         #if DEBUG2
  979.             SysDebugStr ("\pstatus: siDeviceName");
  980.         #endif
  981.             ((SInt32*)pb->csParam)[0] = 0;    //Sound Manager doesn't have to copy any data
  982.             BlockMoveData (gDriverName[0], ((Handle*)pb->csParam)[1], *gDriverName[0]+1);
  983.             break;
  984.         case siLevelMeterOnOff:
  985.             //Get the current state of the level meter. To get the level meter setting, the infoData parameter
  986.             //points to two integers; the first integer indicates the state of the level meter, and the second
  987.             //integer contains the level value of the meter. The level meter setting is an integer that ranges
  988.             //from 0 (no volume) to 255 (full volume). 
  989.         #if DEBUG2
  990.             SysDebugStr ("\pstatus: siLevelMeterOnOff");
  991.         #endif
  992.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  993.             ((SInt16*)pb->csParam)[2] = gLevelMeterOn;
  994.             ((SInt16*)pb->csParam)[3] = 0;
  995.             break;
  996.         case siNumberChannels:
  997.             //Get the number of channels this device is to record. The infoData parameter points to an integer,
  998.             //which indicates the number of channels. Note that this selector determines the format of the data
  999.             //stream output by the driver. If the number of channels is 1, the driver should output monophonic data
  1000.             //in response to a Read call. If the number of channels is 2, the driver should output interleaved
  1001.             //stereo data.
  1002.         #if DEBUG2
  1003.             SysDebugStr ("\pstatus: siNumberChannels");
  1004.         #endif
  1005.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1006.             ((SInt16*)pb->csParam)[2] = gNumberChannels;
  1007.             break;
  1008.         case siRecordingQuality:
  1009.             //Get the current quality of recorded sound. The infoData parameter points to a buffer of type
  1010.             //OSType, which is the recording quality. Currently four qualities are supported, defined by
  1011.             //these constants:
  1012.             //            siCDQuality                            = 'cd  ',                /*44.1kHz, stereo, 16 bit*/
  1013.             //            siBestQuality                        = 'best';                /*22kHz, mono, 8 bit*/
  1014.             //            siBetterQuality                        = 'betr';                /*22kHz, mono, MACE 3:1*/
  1015.             //            siGoodQuality                        = 'good';
  1016.             //These qualities are defined by the sound input device driver. Usually best means monaural,
  1017.             //8-bit, 22 kHz, sound with no compression.
  1018.         #if DEBUG2
  1019.             SysDebugStr ("\pstatus: siRecordingQuality");
  1020.         #endif
  1021.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1022.             ((OSType*)pb->csParam)[1] = gRecordingQuality;
  1023.             break;
  1024.         case siSampleRate:
  1025.             //Get the sample rate to be produced by this device. The sample rate must be in the range 0 to
  1026.             //65535.65535 Hz. The sample rate is declared as a Fixed data type. In order to accommodate sample
  1027.             //rates greater than 32 kHz, the most significant bit is not treated as a sign bit; instead, that
  1028.             //bit is interpreted as having the value 32,768. The infoData parameter points to a buffer of type
  1029.             //Fixed, which is the sample rate.
  1030.         #if DEBUG2
  1031.             SysDebugStr ("\pstatus: siSampleRate");
  1032.         #endif
  1033.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1034.             ((UnsignedFixed*)pb->csParam)[1] = gSampleRate;
  1035.             break;
  1036.         case siSampleRateAvailable:
  1037.             //Get the range of sample rates this device can produce. The infoData parameter points to an integer,
  1038.             //which is the number of sample rates the device supports, followed by a handle. The handle references
  1039.             //a list of sample rates, each of type Fixed. If the device can record a range of sample rates, the
  1040.             //number of sample rates is set to 0 and the handle contains two rates, the minimum and the maximum
  1041.             //of the range of sample rates. Otherwise, a list is returned that contains the sample rates supported.
  1042.             //In order to accommodate sample rates greater than 32 kHz, the most significant bit is not treated as
  1043.             //a sign bit; instead, that bit is interpreted as having the value 32,768.
  1044.         #if DEBUG2
  1045.             SysDebugStr ("\pstatus: siSampleRateAvailable");
  1046.         #endif
  1047.             sampleRateAvailable = NewHandle (sizeof (UnsignedFixed) * kNumRates);
  1048.             if (sampleRateAvailable != nil) {
  1049.                 ((UnsignedFixed*)*sampleRateAvailable)[0] = rate44khz;
  1050.                 ((UnsignedFixed*)*sampleRateAvailable)[1] = rate22050hz;
  1051.                 ((UnsignedFixed*)*sampleRateAvailable)[2] = rate11025hz;
  1052.                 info.count = kNumRates;
  1053.                 info.infoHandle = sampleRateAvailable;
  1054.                 ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList);        //number of bytes being returned
  1055.                 *(SoundInfoList*)(pb->csParam+2) = info;
  1056.             }
  1057.             break;
  1058.         case siSampleSize:
  1059.             //Get the sample size to be produced by this device. Because some compression formats require specific
  1060.             //sample sizes, this selector might return an error when compression is used. The infoData parameter
  1061.             //points to an integer, which is the sample size.
  1062.         #if DEBUG2
  1063.             SysDebugStr ("\pstatus: siSampleSize");
  1064.         #endif
  1065.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1066.             ((SInt16*)pb->csParam)[2] = gSampleSize;
  1067.             break;
  1068.         case siSampleSizeAvailable:
  1069.             //Get the range of sample sizes this device can produce. The infoData parameter points to an integer,
  1070.             //which is the number of sample sizes the device supports, followed by a handle. The handle references
  1071.             //a list of sample sizes, each of type Integer.
  1072.         #if DEBUG2
  1073.             SysDebugStr ("\pstatus: siSampleSizeAvailable");
  1074.         #endif
  1075.             sampleSizeAvailable = NewHandle (sizeof (SInt16) * kNumSizes);
  1076.             if (sampleSizeAvailable != nil) {
  1077.                 ((SInt16*)*sampleSizeAvailable)[0] = 8;
  1078.                 ((SInt16*)*sampleSizeAvailable)[1] = 16;
  1079.                 info.count = kNumSizes;
  1080.                 info.infoHandle = sampleSizeAvailable;
  1081.                 ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList);        //number of bytes being returned
  1082.                 *(SoundInfoList*)(pb->csParam+2) = info;
  1083.             }
  1084.             break;
  1085.         case siTwosComplementOnOff:
  1086.             //Get the current state of the two’s complement feature. This selector only applies to 8-bit data.
  1087.             //(16-bit samples are always stored in two’s complement format.) If on, the driver stores all samples
  1088.             //in the application buffer as two’s complement values (that is, –128 to 127). Otherwise, the driver
  1089.             //stores the samples as offset binary values (that is, 0 to 255). The infoData parameter points to an
  1090.             //integer, which is the current state of the two’s complement feature (1 if two’s complement output
  1091.             //is desired, 0 otherwise).
  1092.         #if DEBUG2
  1093.             SysDebugStr ("\pstatus: siTwosComplementOnOff");
  1094.         #endif
  1095.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1096.             ((SInt16*)pb->csParam)[2] = gTwosComplementOn;
  1097.             break;
  1098.  
  1099.         //Sound Manager only call
  1100.         case siPauseRecording:
  1101.             //The Sound Input Manager uses this selector to get the current pause state.
  1102.             //The sound input device driver continues recording but does not store the sampled
  1103.             //data in a buffer. The infoData parameter points to an integer, which indicates
  1104.             //the state of pausing (0 is off, 1 is on).
  1105.         #if DEBUG2
  1106.             SysDebugStr ("\pcontrol: siPauseRecording");
  1107.         #endif
  1108.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1109.             ((SInt16*)pb->csParam)[2] = gPauseState;
  1110.             break;
  1111.  
  1112.         //Status calls that can be optionally supported
  1113.         case siActiveChannels:
  1114.             //Get the channels to record from. When reading the active channels, the data returned is
  1115.             //a bitmap of the active channels.
  1116.         #if DEBUG2
  1117.             SysDebugStr ("\pstatus: siActiveChannels");
  1118.         #endif
  1119.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1120.             ((SInt32*)pb->csParam)[1] = gActiveChannels;
  1121.             break;
  1122.         case siActiveLevels:
  1123.             //Get the current signal level for each active channel. The infoData parameter points to an array
  1124.             //of integers, the size of which depends on the number of active channels. You can determine how
  1125.             //many channels are active by calling SPBGetDeviceInfo with the siNumberChannels selector.
  1126.         #if DEBUG2
  1127.             SysDebugStr ("\pstatus: siActiveLevels");
  1128.         #endif
  1129.             if (gNumberChannels == 1) {
  1130.                 ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1131.                 ((SInt16*)pb->csParam)[2] = 0;
  1132.             } else if (gNumberChannels == 2) {
  1133.                 ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1134.                 ((SInt16*)pb->csParam)[2] = 0;
  1135.                 ((SInt16*)pb->csParam)[3] = 0;
  1136.             }
  1137.             break;
  1138.         case siAGCOnOff:
  1139.             //Get the current state of the automatic gain control feature. The infoData parameter points to an
  1140.             //integer, which is 0 if gain control is off and 1 if it is on.
  1141.         #if DEBUG2
  1142.             SysDebugStr ("\pstatus: siAGCOnOff");
  1143.         #endif
  1144.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1145.             ((SInt16*)pb->csParam)[2] = gAGCOn;
  1146.             break;
  1147.         case siCompressionHeader:
  1148.             //Get a compressed sound header for the current recording settings. Your application passes in a
  1149.             //pointer to a compressed sound header and the driver fills it in. Before calling SPBGetDeviceInfo
  1150.             //with this selector, you should set the numFrames field of the compressed sound header to the number
  1151.             //of bytes in the sound. When SPBGetDeviceInfo returns successfully, that field contains the number
  1152.             //of sample frames in the sound. This selector is needed only by drivers that use compression types
  1153.             //that are not directly supported by Apple. If you call this selector after recording a sound, your
  1154.             //application can get enough information about the sound to play it or save it in a file. The infoData
  1155.             //parameter points to a compressed sound header.
  1156.         #if DEBUG2
  1157.             SysDebugStr ("\pstatus: siCompressionHeader");
  1158.         #endif
  1159.             err = siUnknownInfoType;    //we don't support compression
  1160.             break;
  1161.         case siCompressionNames:
  1162.             //Get a list of names of the compression types supported by the sound input device. In response to a
  1163.             //Status call, a sound input device driver returns, in the location specified by the infoData parameter,
  1164.             //a handle to a block of memory that contains the names of all compression types supported by the driver.
  1165.             //It is the driver’s responsibility to allocate that block of memory, but it should not release it. The
  1166.             //software issuing this selector is responsible for disposing of the handle. As a result, a device driver
  1167.             //must detach any resource handles (by calling DetachResource) before returning them to the caller. The
  1168.             //data in the handle has the same format as an 'STR#' resource: a two-byte count of the strings in the
  1169.             //resource, followed by the strings themselves. The strings should occur in the same order as the
  1170.             //compression types returned by the siCompressionAvailable selector. If the driver does not support
  1171.             //compression, it returns siUnknownInfoType. If the driver supports compression but for some reason not
  1172.             //all compression types are currently selectable, it returns a list of all available compression types.
  1173.         #if DEBUG2
  1174.             SysDebugStr ("\pstatus: siCompressionNames");
  1175.         #endif
  1176.             err = siUnknownInfoType;    //we don't support compression
  1177.             break;
  1178.         case siInputGain:
  1179.             //Get the current sound input gain. If the available hardware allows adjustment of the recording gain,
  1180.             //this selector lets you get and set the gain. In response to a Status call, a sound input driver returns
  1181.             //the current gain setting. The infoData parameter points to a 4-byte value of type Fixed ranging from
  1182.             //0.5 to 1.5, where 1.5 specifies maximum gain.
  1183.         #if DEBUG2
  1184.             SysDebugStr ("\pstatus: siInputGain");
  1185.         #endif
  1186.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1187.             ((Fixed*)pb->csParam)[1] = gInputGain;
  1188.             break;
  1189.         case siInputSource:
  1190.             //Get the current sound input source. If the available hardware allows recording from more than one
  1191.             //source, this selector lets you get and set the source. In response to a Status call, a sound input
  1192.             //driver returns the current source value; if the driver supports only one source, it returns
  1193.             //siUnknownInfoType.
  1194.         #if DEBUG2
  1195.             SysDebugStr ("\pstatus: siInputSource");
  1196.         #endif
  1197.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1198.             ((SInt16*)pb->csParam)[2] = gCurrentInputSource;
  1199.             break;
  1200.         case siInputSourceNames:
  1201.             //Get a list of the names of all the sound input sources supported by the sound input device. In
  1202.             //response to a Status call, a sound input device driver returns, in the location specified by the
  1203.             //infoData parameter, a handle to a block of memory that contains the names of all sound sources
  1204.             //supported by the driver. It is the driver’s responsibility to allocate that block of memory, but it
  1205.             //should not release it. The software issuing this selector is responsible for disposing of the handle.
  1206.             //As a result, a device driver must detach any resource handles (by calling DetachResource) before
  1207.             //returning them to the caller. The data in the handle has the same format as an 'STR#' resource: a
  1208.             //two-byte count of the strings in the resource, followed by the strings themselves. The strings should
  1209.             //occur in the same order as the input sources returned by the siInputSource selector. If the driver
  1210.             //supports only one source, it returns siUnknownInfoType. If the driver supports more than one source
  1211.             //but for some reason not all of them are currently selectable, it returns a list of all available
  1212.             //input sources.
  1213.         #if DEBUG2
  1214.             SysDebugStr ("\pstatus: siInputSourceNames");
  1215.         #endif
  1216.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1217.             ((Handle*)pb->csParam)[1] = gInputSourceNames;
  1218.             break;
  1219.         case siOptionsDialog:
  1220.             //Determine whether the driver supports an Options dialog box (SPBGetDeviceInfo). This dialog box is
  1221.             //designed to allow the user to configure device-specific features of the sound input hardware. With
  1222.             //SPBGetDeviceInfo, the infoData parameter points to an integer, which indicates whether the driver
  1223.             //supports an Options dialog box (1 if it supports it, 0 otherwise).
  1224.         #if DEBUG2
  1225.             SysDebugStr ("\pstatus: siOptionsDialog");
  1226.         #endif
  1227.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1228.             ((SInt16*)pb->csParam)[2] = 1;    //we have a dialog
  1229.             break;
  1230.         case siPlayThruOnOff:
  1231.             //Get the current play-through state and volume. The infoData parameter points to an integer,
  1232.             //which indicates the current play-through volume (1 to 7). If that integer is 0, then
  1233.             //play-through is off.
  1234.         #if DEBUG2
  1235.             SysDebugStr ("\pstatus: siPlayThruOnOff");
  1236.         #endif
  1237.             ((SInt32*)pb->csParam)[0] = 2;    //number of bytes being returned
  1238.             ((SInt16*)pb->csParam)[2] = gPlayThruVolume;
  1239.             break;
  1240.         case siStereoInputGain:
  1241.             //Get the current stereo sound input gain. If the available hardware allows adjustment of the recording
  1242.             //gain, this selector lets you get and set the gain for each of two channels (left or right). In response
  1243.             //to a Status call, a sound input driver should return the current gain setting for the specified channel.
  1244.             //The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5, where 1.5
  1245.             //specifies maximum gain. The first of these values is equivalent to the gain for the left channel and the
  1246.             //second value is equivalent to the gain for the right channel.
  1247.         #if DEBUG2
  1248.             SysDebugStr ("\pstatus: siStereoInputGain");
  1249.         #endif
  1250.             ((SInt32*)pb->csParam)[0] = 8;    //number of bytes being returned
  1251.             ((Fixed*)pb->csParam)[1] = gLeftInputGain;
  1252.             ((Fixed*)pb->csParam)[2] = gRightInputGain;
  1253.             break;
  1254.         case siVoxRecordInfo:
  1255.             //Get the current VOX recording parameters. The infoData parameter points to two integers. The first
  1256.             //integer indicates whether VOX recording is on or off (0 if off, 1 if on). The second integer indicates
  1257.             //the VOX record trigger value. Trigger values range from 0 to 255 (0 is trigger immediately, 255 is
  1258.             //trigger only on full volume).
  1259.         #if DEBUG2
  1260.             SysDebugStr ("\pstatus: siVoxRecordInfo");
  1261.         #endif
  1262.             ((SInt32*)pb->csParam)[0] = 4;    //number of bytes being returned
  1263.             ((SInt16*)pb->csParam)[2] = gVoxRecordingOn;
  1264.             ((SInt16*)pb->csParam)[3] = gVOXStartTrigger;
  1265.             break;
  1266.         case siVoxStopInfo:
  1267.             //Get the current VOX stopping parameters. The infoData parameter points to three integers. The
  1268.             //first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The second integer
  1269.             //indicates the VOX stop trigger value. Trigger values range from 0 to 255 (255 is stop immediately, 0
  1270.             //is stop only on total silence). The third integer indicates how many milliseconds the trigger value must
  1271.             //be continuously valid for recording to be stopped. Delay values range from 0 to 65,535.
  1272.         #if DEBUG2
  1273.             SysDebugStr ("\pstatus: siVoxStopInfo");
  1274.         #endif
  1275.             ((SInt32*)pb->csParam)[0] = 6;    //number of bytes being returned
  1276.             ((SInt16*)pb->csParam)[2] = gVoxStoppingOn;
  1277.             ((SInt16*)pb->csParam)[3] = gVOXStopTrigger;
  1278.             ((SInt16*)pb->csParam)[4] = gVOXDelay;
  1279.             break;
  1280.  
  1281.         //Unknown or unsupported status call
  1282.         default:
  1283.         #if DEBUG
  1284.             SysDebugStr ("\pstatus: unknown selector");
  1285.         #endif
  1286.             err = statusErr;
  1287.     }
  1288.  
  1289. #if DEBUG
  1290.     if (err != noErr && err != statusErr && err != siUnknownInfoType) {
  1291.         SysDebugStr ("\p!!Error in DriverStatusCmd!!");
  1292.     }
  1293. #if FULLDEBUG
  1294.     SysDebugStr ("\pleaving DriverStatusCmd;g");
  1295. #endif
  1296. #endif
  1297.  
  1298.     return (err);
  1299. }
  1300.  
  1301. /*
  1302.     May run at interrupt level, CANNOT allocate or move memory.
  1303. */
  1304. static OSStatus DriverReadCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) {
  1305. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  1306.     OSStatus            err = noErr;
  1307.     double_t            samplesInBuffer = kSamplesInBuffer,
  1308.                         temp, temp2;
  1309.  
  1310. #if DEBUG
  1311.     SysDebugStr ("\pin DriverReadCmd;g");
  1312. #endif
  1313.  
  1314.     gSimulatedHWInterrupt.theTask.tmAddr = gSimulatedHWIntProc;
  1315.     gSimulatedHWInterrupt.theTask.tmWakeUp = 0;
  1316.     gSimulatedHWInterrupt.pb = pb;
  1317.  
  1318.     temp = ((gSampleRate >> 16) / samplesInBuffer);
  1319.  
  1320.     temp2 = -(1000000.0 / temp);    //calculate time in microseconds
  1321.     gInterruptFreq = temp2;
  1322.  
  1323.     InsXTime ((QElemPtr)&gSimulatedHWInterrupt);
  1324.     PrimeTime ((QElemPtr)&gSimulatedHWInterrupt, gInterruptFreq);    //wait for the time it takes to "record" one buffer
  1325.  
  1326.     err = ioInProgress;
  1327.  
  1328. #if DEBUG
  1329.     if (err != noErr && err != ioInProgress) {
  1330.         SysDebugStr ("\p!!Error in DriverReadCmd!!");
  1331.     }
  1332. #if FULLDEBUG
  1333.     SysDebugStr ("\pleaving DriverReadCmd;g");
  1334. #endif
  1335. #endif
  1336.  
  1337.     return (err);
  1338. }
  1339.  
  1340. /*
  1341.     May run at interrupt level, CANNOT allocate or move memory.
  1342. */
  1343. static OSStatus DriverWriteCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) {
  1344. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind, pb)
  1345.     OSStatus            err;
  1346.  
  1347. #if DEBUG
  1348.     SysDebugStr ("\pin DriverWriteCmd;g");
  1349. #endif
  1350.  
  1351.     //Sound input drivers don't have a write command.
  1352.     err = paramErr;
  1353.  
  1354. #if DEBUG
  1355.     if (err != noErr) {
  1356.         SysDebugStr ("\p!!Error in DriverWriteCmd!!");
  1357.     }
  1358. #if FULLDEBUG
  1359.     SysDebugStr ("\pleaving DriverWriteCmd;g");
  1360. #endif
  1361. #endif
  1362.  
  1363.     return (err);
  1364. }
  1365.  
  1366. /*
  1367.     May run at interrupt level, CANNOT allocate or move memory.
  1368. */
  1369. static OSStatus DriverKillIOCmd (ParmBlkPtr pb) {
  1370. #pragma unused (pb)
  1371.     OSStatus            err = noErr;
  1372.  
  1373. #if DEBUG
  1374.     SysDebugStr ("\pin DriverKillIOCmd");
  1375. #endif
  1376.  
  1377.     gStopRecording = true;    //when our next hardware "interrupt" fires we will stop the "hardware"
  1378.  
  1379. #if DEBUG
  1380.     if (err != noErr) {
  1381.         SysDebugStr ("\p!!Error in DriverKillIOCmd!!");
  1382.     }
  1383. #if FULLDEBUG
  1384.     SysDebugStr ("\pleaving DriverKillIOCmd;g");
  1385. #endif
  1386. #endif
  1387.  
  1388.     return (err);
  1389. }
  1390.  
  1391. /*
  1392.     May run at interrupt level, CANNOT allocate or move memory.
  1393. */
  1394. extern OSErr DoDriverIO(AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind) {
  1395.     OSStatus        status;
  1396.     OSErr            err        = noErr;
  1397.  
  1398. #if FULLDEBUG
  1399.     SysDebugStr ("\pin DoDriverIO;g");
  1400. #endif
  1401.  
  1402.     /*
  1403.         Note: Initialize, Open, KillIO, Close, and Finalize are either synchronous
  1404.         or immediate. Read, Write, Control, and Status may be immediate,
  1405.         synchronous, or asynchronous.
  1406.     */
  1407.     switch (ioCommandCode) {
  1408.         case kInitializeCommand:        /* Always immediate */
  1409.             status = DriverInitializeCmd (addressSpaceID, ioCommandContents.initialInfo);
  1410.             break;
  1411.         case kFinalizeCommand:            /* Always immediate */
  1412.             status = DriverFinalizeCmd (ioCommandContents.finalInfo);
  1413.             break;
  1414.         case kSupersededCommand:        /* Always immediate */
  1415.             status = DriverSupersededCmd (ioCommandContents.supersededInfo);
  1416.             break;
  1417.         case kReplaceCommand:            /* Always immediate, replace an old driver */
  1418.             status = DriverReplaceCmd (addressSpaceID, ioCommandContents.replaceInfo);
  1419.             break;
  1420.         case kOpenCommand:                /* Always immediate */
  1421.             status = DriverOpenCmd (addressSpaceID, ioCommandContents.pb);
  1422.             break;
  1423.         case kCloseCommand:                /* Always immediate */
  1424.             status = DriverCloseCmd (ioCommandContents.pb);
  1425.             break;
  1426.         case kControlCommand:
  1427.             status = DriverControlCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb);
  1428.             break;
  1429.         case kStatusCommand:
  1430.             status = DriverStatusCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb);
  1431.             break;
  1432.         case kReadCommand:
  1433.             status = DriverReadCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb);
  1434.             break;
  1435.         case kWriteCommand:
  1436.             status = DriverWriteCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb);
  1437.             break;
  1438.         case kKillIOCommand:            /* Always immediate */
  1439.             status = DriverKillIOCmd (ioCommandContents.pb);
  1440.             break;
  1441.         default:
  1442.             status = paramErr;
  1443.             break;
  1444.     }
  1445.     if ((ioCommandKind & kImmediateIOCommandKind) != 0) {
  1446.         /* Immediate commands return the operation status and don't call IOCommandIsComplete */
  1447.     } else if (status == ioInProgress) {
  1448.         /* Perform the action and call IOCommandIsComplete at some later time */
  1449.         status = noErr;
  1450.     } else {
  1451.         err = status;
  1452.         status = IOCommandIsComplete (ioCommandID, err);
  1453.     }
  1454.  
  1455. #if DEBUG
  1456.     if (err == paramErr) {
  1457.         SysDebugStr ("\p!!Param Error in DoDriverIO!!");
  1458.     }
  1459. #if FULLDEBUG
  1460.     SysDebugStr ("\pleaving DoDriverIO;g");
  1461. #endif
  1462. #endif
  1463.  
  1464.     return (status);
  1465. }
  1466.  
  1467. static pascal void HWIntProc (QElemPtr passedPtr) {
  1468.     UInt32                    numSamples,
  1469.                             numBytesLeftToRecord;
  1470.     Boolean                    recordingComplete    = false;
  1471.     ParmBlkPtr                pb = ((myTMTask*)passedPtr)->pb;
  1472.  
  1473. #if DEBUG
  1474.     SysDebugStr ("\pin HWIntProc");
  1475. #endif
  1476.  
  1477.     if (gStopRecording == false) {
  1478.         numBytesLeftToRecord = pb->ioParam.ioReqCount - pb->ioParam.ioActCount;
  1479.  
  1480.         //If the number of bytes requested is larger than our buffer, return the max number of samples that will fit in our buffer
  1481.         if (numBytesLeftToRecord > (kSamplesInBuffer * gNumberChannels * (gSampleSize/8))) {
  1482.             numSamples = kSamplesInBuffer;
  1483.         } else {
  1484.             //otherwise figure out how many samples the requested number of bytes equals
  1485.             numSamples = numBytesLeftToRecord / (gNumberChannels * (gSampleSize/8));
  1486.         }
  1487.  
  1488.         switch (gCurrentInputSource) {
  1489.             case 1:
  1490.                 //Make a square wave
  1491.                 MakeSquareWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels);
  1492.                 break;
  1493.             case 2:
  1494.                 //Make a saw tooth wave
  1495.                 MakeSawWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels);
  1496.                 break;
  1497.             case 3:
  1498.                 //Make silence
  1499.                 MakeSquareWave (0, numSamples, gSampleRate, 0, gSampleSize, gNumberChannels);
  1500.                 break;
  1501.             default:
  1502.             #if DEBUG
  1503.                 SysDebugStr ("\punknown input source in HWIntProc");
  1504.             #endif
  1505.                 break;    //null statement so when DEBUG is false this switch still compiles
  1506.         }
  1507.  
  1508.         if (gTwosComplementOn == true && gSampleSize == 8) {
  1509.             SInt32 i;
  1510.             for (i = 0; i < numSamples; i++) {
  1511.                 ((UInt32*)gSoundBuffer)[i] ^= 0x80808080;
  1512.             }
  1513.         }
  1514.  
  1515.         if (gUserInterruptProc != nil) {
  1516.             CallSIInterruptProc (gUserInterruptProc, pb, gSoundBuffer, 0, gSampleSize);
  1517.         }
  1518.  
  1519.         //if we have a buffer and pausing is off, copy data to app's buffer
  1520.         if (pb->ioParam.ioBuffer != nil && gPauseState == 0) {
  1521.             BlockMoveData (gSoundBuffer, pb->ioParam.ioBuffer+pb->ioParam.ioActCount, numSamples*(gSampleSize/8)*gNumberChannels);
  1522.         }
  1523.  
  1524.         //If we are not paused, update the number of samples we have made
  1525.         if (gPauseState == 0) {
  1526.             pb->ioParam.ioActCount += numSamples*(gSampleSize/8)*gNumberChannels;
  1527.         }
  1528.  
  1529.         if (pb->ioParam.ioActCount == pb->ioParam.ioReqCount) {
  1530.             recordingComplete = true;
  1531.         }
  1532.     }
  1533.  
  1534.     if (recordingComplete == false && gStopRecording == false) {
  1535.         PrimeTime ((QElemPtr)passedPtr, gInterruptFreq);    //more data to record
  1536.     } else {
  1537.         //Have recorded requested number of bytes, stop the hardware (if continuous recording is not on)
  1538.         //Do a software interrupt so that we can call IOCommandIsComplete (which can only be called at
  1539.         //task level and software interrupt level.
  1540.         RmvTime ((QElemPtr)passedPtr);
  1541.         if (gStopRecording == true) {
  1542.             SetHardwareToDefault ();
  1543.         } else if (gContinuousOn == false) {
  1544.             gSamplesWritten = 0;
  1545.         }
  1546.         IOCommandIsComplete ((IOCommandID)pb->ioParam.ioCmdAddr, noErr);
  1547.     }
  1548.  
  1549. #if FULLDEBUG
  1550.     SysDebugStr ("\pleaving HWIntProc;g");
  1551. #endif
  1552. }
  1553.  
  1554. //Makes a square wave of the specified frequency and duration
  1555. //duration in 1/100 of a second, i.e. 500 for a five second tone
  1556. //frequency in samples per second, i.e. 8000 for an 8000Hz square wave (requires 16kHz sampling rate)
  1557. static void MakeSquareWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) {
  1558.     SInt32            numSamples    = 0;
  1559.  
  1560.     if (duration != 0 && numberSamples == 0) {
  1561.         numSamples = (duration / 100.0) * (sampleRate >> 16);
  1562.     } else if (duration == 0 && numberSamples != 0) {
  1563.         numSamples = numberSamples;
  1564.     }
  1565.  
  1566.     if (numSamples > 0) {
  1567.         if (frequency == 0) {
  1568.             //Generate silence of requested length
  1569.             if (sampleSize == 8) {
  1570.                 UInt8 *buffer8 = (UInt8*)gSoundBuffer;
  1571.                 UInt8 *end8 = buffer8 + (numSamples * numChannels);
  1572.                 while (buffer8 < end8) {
  1573.                     *buffer8++ = kSilence;
  1574.                 }
  1575.             } else if (sampleSize == 16) {
  1576.                 UInt16 *buffer16 = (UInt16*)gSoundBuffer;
  1577.                 UInt16 *end16 = buffer16 + (numSamples * numChannels);
  1578.                 while (buffer16 < end16) {
  1579.                     *buffer16++ = 0;
  1580.                 }
  1581.             } else {
  1582.             #if DEBUG
  1583.                 SysDebugStr ("\pbad sample size in MakeSquareWave (silence)");
  1584.             #endif
  1585.             }
  1586.         } else if ((frequency * 2) <= sampleRate) {
  1587.             //Generate a square wave
  1588.             SInt32    samplesBeforeReverse =     (((sampleRate >> 16) / frequency) / 2) * numChannels;
  1589.  
  1590.             if (sampleSize == 8) {
  1591.                 UInt8 *buffer8 = (UInt8*)gSoundBuffer;
  1592.                 UInt8 *end8 = buffer8 + (numSamples * numChannels);
  1593.                 while (buffer8 < end8) {
  1594.                     if (gSamplesWritten < samplesBeforeReverse) {
  1595.                         *buffer8++ = kSilence + kAmplitude;
  1596.                         gSamplesWritten++;
  1597.                     } else {
  1598.                         *buffer8++ = kSilence - kAmplitude;
  1599.                         if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
  1600.                             gSamplesWritten = 0;
  1601.                         } else {
  1602.                             gSamplesWritten++;
  1603.                         }
  1604.                     }
  1605.                 }
  1606.             } else if (sampleSize == 16) {
  1607.                 UInt16 *buffer16 = (UInt16*)gSoundBuffer;
  1608.                 UInt16 *end16 = buffer16 + (numSamples * numChannels);
  1609.                 while (buffer16 < end16) {
  1610.                     if (gSamplesWritten < samplesBeforeReverse) {
  1611.                         *buffer16++ = (kAmplitude * 0.01) * 32767;
  1612.                         gSamplesWritten++;
  1613.                     } else {
  1614.                         *buffer16++ = -((kAmplitude * 0.01) * 32767);
  1615.                         if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
  1616.                             gSamplesWritten = 0;
  1617.                         } else {
  1618.                             gSamplesWritten++;
  1619.                         }
  1620.                     }
  1621.                 }
  1622.             } else {
  1623.             #if DEBUG
  1624.                 SysDebugStr ("\pbad sample size in MakeSquareWave (square wave)");
  1625.             #endif
  1626.             }
  1627.         }
  1628.     }
  1629. }
  1630.  
  1631. static void MakeSawWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) {
  1632.     SInt32            numSamples    = 0;
  1633.  
  1634.     if (duration != 0 && numberSamples == 0) {
  1635.         numSamples = (duration / 100.0) * (sampleRate >> 16);
  1636.     } else if (duration == 0 && numberSamples != 0) {
  1637.         numSamples = numberSamples;
  1638.     }
  1639.  
  1640.     if (numSamples > 0) {
  1641.         if ((frequency * 2) <= sampleRate) {
  1642.             //Generate a saw tooth wave
  1643.             SInt32    samplesBeforeReverse =     (((sampleRate >> 16) / frequency) / 2);
  1644.  
  1645.             if (sampleSize == 8) {
  1646.                 UInt8 *buffer8 = (UInt8*)gSoundBuffer;
  1647.                 UInt8 *end8 = buffer8 + (numSamples * numChannels);
  1648.                 UInt8 increment = (kAmplitude*2) / samplesBeforeReverse;
  1649.                 while (buffer8 < end8) {
  1650.                     if (gSamplesWritten < samplesBeforeReverse) {
  1651.                         *buffer8++ = gNextSaw8Sample;
  1652.                         if (numChannels == 2) {
  1653.                             *buffer8++ = gNextSaw8Sample;
  1654.                         }
  1655.                         gNextSaw8Sample += increment;
  1656.                         gSamplesWritten++;
  1657.                     } else {
  1658.                         *buffer8++ = gNextSaw8Sample;
  1659.                         if (numChannels == 2) {
  1660.                             *buffer8++ = gNextSaw8Sample;
  1661.                         }
  1662.                         gNextSaw8Sample -= increment;
  1663.                         if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
  1664.                             gSamplesWritten = 0;
  1665.                         } else {
  1666.                             gSamplesWritten++;
  1667.                         }
  1668.                     }
  1669.                 }
  1670.             } else if (sampleSize == 16) {
  1671.                 UInt16 *buffer16 = (UInt16*)gSoundBuffer;
  1672.                 UInt16 *end16 = buffer16 + (numSamples * numChannels);
  1673.                 UInt16 increment = (((kAmplitude * 0.01) * 32767)*2) / samplesBeforeReverse;
  1674.                 while (buffer16 < end16) {
  1675.                     if (gSamplesWritten < samplesBeforeReverse) {
  1676.                         *buffer16++ = gNextSaw16Sample;
  1677.                         if (numChannels == 2) {
  1678.                             *buffer16++ = gNextSaw16Sample;
  1679.                         }
  1680.                         gNextSaw16Sample += increment;
  1681.                         gSamplesWritten++;
  1682.                     } else {
  1683.                         *buffer16++ = gNextSaw16Sample;
  1684.                         if (numChannels == 2) {
  1685.                             *buffer16++ = gNextSaw16Sample;
  1686.                         }
  1687.                         gNextSaw16Sample -= increment;
  1688.                         if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
  1689.                             gSamplesWritten = 0;
  1690.                         } else {
  1691.                             gSamplesWritten++;
  1692.                         }
  1693.                     }
  1694.                 }
  1695.             } else {
  1696.             #if DEBUG
  1697.                 SysDebugStr ("\pbad sample size in MakeSawWave");
  1698.             #endif
  1699.             }
  1700.         }
  1701.     }
  1702. }
  1703.  
  1704. static OSErr        DoOptionsDialog    (void) {
  1705.     FSSpec                driverFSSpec;
  1706.     Rect                rect;
  1707.     ControlHandle        item        = nil;
  1708.     DialogRef            optionsDialog;
  1709.     OSErr                err;
  1710.     SInt16                itemHit        = 0,
  1711.                         itemType,
  1712.                         curRes,
  1713.                         resRef        = -1,
  1714.                         newInputSource = gCurrentInputSource;
  1715.     Boolean                wasChanged,
  1716.                         done        = false;
  1717.  
  1718.     curRes = CurResFile ();
  1719.     err = ResolveAlias (nil, gAliasToDriver, &driverFSSpec, &wasChanged);
  1720.     if (err == noErr) {
  1721.         resRef = FSpOpenResFile (&driverFSSpec, fsRdPerm);
  1722.         if (resRef != -1) {
  1723.             optionsDialog = GetNewDialog (128, nil, (GrafPtr)-1L);
  1724.         } else {
  1725.         #if DEBUG
  1726.             SysDebugStr ("\pFSpOpenResFile failed");
  1727.         #endif
  1728.         }
  1729.     } else {
  1730.     #if DEBUG
  1731.         SysDebugStr ("\pResolveAlias failed");
  1732.     #endif
  1733.     }
  1734.  
  1735.     if (optionsDialog != nil) {
  1736.         //gCurrentInputSource+5 is the item number of the corresponding radio buttons
  1737.         //so itemHit-5 is the input source number
  1738.         GetDialogItem (optionsDialog, gCurrentInputSource+5, &itemType, &(Handle)item, &rect);
  1739.         if (item != nil) {
  1740.             SetControlValue (item, 1);
  1741.             SetDialogDefaultItem (optionsDialog, 1);    //put the ring around the OK button
  1742.         } else {
  1743.         #if DEBUG
  1744.             SysDebugStr ("\pitem is nil!");
  1745.         #endif
  1746.         }
  1747.  
  1748.         ShowWindow (optionsDialog);
  1749.  
  1750.         do {
  1751.             ModalDialog (nil, &itemHit);
  1752.             switch (itemHit) {
  1753.                 case 1:        //OK button
  1754.                     gCurrentInputSource = newInputSource;
  1755.                     done = true;
  1756.                     break;
  1757.                 case 2:        //Cancel button
  1758.                     done = true;
  1759.                     break;
  1760.                 case 6:        //Square wave
  1761.                 case 7:        //Saw wave
  1762.                 case 8:        //Silence
  1763.                     newInputSource = itemHit-5;
  1764.                     GetDialogItem (optionsDialog, 6, &itemType, &(Handle)item, &rect);
  1765.                     SetControlValue (item, itemHit == 6);
  1766.                     GetDialogItem (optionsDialog, 7, &itemType, &(Handle)item, &rect);
  1767.                     SetControlValue (item, itemHit == 7);
  1768.                     GetDialogItem (optionsDialog, 8, &itemType, &(Handle)item, &rect);
  1769.                     SetControlValue (item, itemHit == 8);
  1770.                     break;
  1771.             }
  1772.         } while (!done);
  1773.  
  1774.         DisposeDialog (optionsDialog);
  1775.         optionsDialog = nil;
  1776.     } else {
  1777.     #if DEBUG
  1778.         SysDebugStr ("\poptionsDialog is nil!");
  1779.     #endif
  1780.     }
  1781.  
  1782.     if (resRef != -1) {
  1783.         CloseResFile (resRef);
  1784.         UseResFile (curRes);
  1785.     }
  1786.  
  1787.     return err;
  1788. }
  1789.  
  1790. static void SetHardwareToDefault (void) {
  1791.     gContinuousOn            = 0;
  1792.     gLevelMeterOn            = 0;
  1793.     gTwosComplementOn        = 0;
  1794.     gAGCOn                    = 0;
  1795.     gPlayThruVolume            = 0;
  1796.     gVoxRecordingOn            = 0;
  1797.     gVoxStoppingOn            = 0;
  1798.     gPauseState                = 0;
  1799.     gNumberChannels            = 1;
  1800.     gSampleSize                = 8;
  1801.     gVOXStartTrigger        = 0;
  1802.     gVOXStopTrigger            = 0;
  1803.     gVOXDelay                = 0;
  1804.     gCompressionFactor        = 1;
  1805. //    gCurrentInputSource        = 1;    //don't change the current input source
  1806.     gActiveChannels            = 0x00000001;
  1807.     gSampleRate                = rate22050hz;
  1808.     gInputGain                = 0x00010000;
  1809.     gLeftInputGain            = 0x01000100;
  1810.     gRightInputGain            = 0x01000100;
  1811.     gCompressionType        = 'NONE';
  1812.     gRecordingQuality        = siBestQuality;
  1813.     gUserInterruptProc        = nil;
  1814.     gSamplesWritten            = 0;
  1815.     gNextSaw8Sample            = kSilence - kAmplitude;
  1816.     gNextSaw16Sample        = -((kAmplitude * 0.01) * 32767);
  1817.     gStopRecording            = false;
  1818. }
  1819.  
  1820.